8822E: fix MCS4+/48M+ TX, resolve the 0x41e8 RX-desense quirk, port kernel channel/runtime obligations + quirks doc (#238)#268
Merged
Conversation
…nel/runtime obligations (#238) Root causes found by golden end-state diff (kernel via /proc read_reg live vs devourer via new DEVOURER_BB_DUMP, 0x000..0x4ffc) + subset bisection with a Jaguar3 DEVOURER_REPLAY_WSEQ port, kernel-TX ground truth on the same unit. Fixes (all bench-validated, 8812EU->8822CU ch36 + SDR): - MCS4+/48M+ whole-PPDU TX corruption: route the DPDT antenna switch to WL control (REG_LED_CFG 0x4c[24] = BIT_DPDT_WLBT_SEL, [22] clear; halmac pinmux family devourer's hand-rolled MAC init skipped) + single-path OFDM 1SS TX routing (0x820: 1ss-B@5G / 1ss-A@2G, kernel semantics; the old 1ss-on-AB mapping interacts with the DPDT fix and wedges MCS0). MCS7 goes 0 -> ~5000 clean/12s EVM -48; full ladder MCS0/4/7/54M clean. - 0x41e8 path-B RX-desense quirk RESOLVED: it was a downstream artifact of the same front-end mis-config, not TSSI asymmetry. Structural skip retired; path-B OFDM TX power is controllable in TX+RX mode again (full-duplex validated: 24k RX frames + 14.3k clean MCS7 simultaneously). - RF reg 0x0 writes: direct-window writes silently no-op; route through the legacy FON port 0x1808/0x4108 (addr<<20|data) in Halrf8822e, Halrf8822c and the radio-table loader (vendor tables carry reg-0 entries). - Construction-time TX-power state was clobbered by the FW power/coex H2Cs: authoritative TXAGC re-apply now runs post-coex (with readback diff log); PAD_CTRL1[29:28] re-asserted there too. - Channel-switch ports from the kernel switch_channel tail: spur elimination (14 harmonic channel/BW combos, NBI+CSI mask; FastRetune declines spur hops), CCK TX shaping filter (per-2G-channel, ch14 special), IGI toggle on BOTH Jaguar3 variants (fixes the intermittent post-switch RX deafness / MCS0 first-cell TX wedge; CU ch6 ground liveness now 5/5). - LCK synthesizer re-lock (AACK+RTK on >=4-unit thermal drift) wired into the coex-tick thermal tracker; fired live in a 22-min max-duty soak (1.10M clean MCS7 frames, EVM drift +3 dB). Verified non-gaps: TSSI subsystem (kernel runs THERMAL mode here, 0x1e7c[30] = 0 both drivers), thermal codeword LUT (end-state identical), TX descriptor (48B, byte-identical). 2.4 GHz TX airs undecodable frames under BOTH drivers (kernel-parity, module property) -- README row corrected, quirk documented. Docs: docs/8822e-quirks.md (the issue deliverable -- every quirk root-caused or kernel-parity-proven, with reproducers). Diag: replay hook at end of both bring-ups, DEVOURER_BB_DUMP, coex-tick TXAGC register watch, DEVOURER_TX_PWR_OFFSET_QDB on txdemo. Tests: 14 new harnesses (tests/eu_*.sh, cu_2g_ground_liveness.sh, sdr_burst_len.py); txpwr_offset_regcheck.sh path-B cell inverted to the new semantics. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…D/EDCCA) The vendor watchdog's RX-side adaptation loop, run every ~2 s like the kernel's: false-alarm/CCA window statistics (read + reset, phydm_fa_cnt_statistics_jgr3), unlinked DIG (IGI stepping in 0x1d70 by FA level, DFS channels pinned to 0x20), CCK packet-detection type4 (CCK-FA moving average driving the PD/CS ladder in 0x1ac8/0x1acc/0x1ad0), and EDCCA threshold tracking (0x84c, th_l2h = max(IGI+8, 48) / -8 — matches the kernel's end state; suppressed while SetCcaMode's EDCCA-disable is active). The DIG coverage window floors at 0x1e, not phydm's generic DIG_MIN_COVERAGE 0x1c: at IGI 0x1c the 8822CU's dense-QAM RX decodes NOTHING (0 of 65k kernel-injected MCS7 frames; 0x1e passes 65.9k) — hardware-bisected, value-specific, the same class of per-IC "For HW setting" floor exception the kernel carries for other ICs. TX/TX+RX sessions tick from the coex thread; RX-only sessions get a dedicated housekeeping thread — register I/O cannot run on the bulk-IN event thread (a sync control transfer there starves its own event loop). The kernel's remaining watchdog mechanisms self-disable without a link and are intentionally absent: CFO tracking (returns unless associated), RA and the beamforming watchdog (no station entries), antenna diversity (2T2R). Matched-power TX EVM parity verified (tests/eu_matched_power_evm.sh, one ground session, refs bit-identical at devourer offset -44 == kernel `txpower fixed 500`): MCS7 median EVM -60 both drivers; MCS0 -64 vs -66. Default-power ladder unchanged (MCS7 6407 clean / -48, MCS0 5680 / -27). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
josephnef
pushed a commit
that referenced
this pull request
Jul 15, 2026
…less under the static DPDT route (#289) ## Summary The 8822E front-end fix (#268 / b5a6df7) routes the DPDT antenna transfer switch with a static two-bit write: `REG_LED_CFG 0x4c[24]` set, `[22]` clear. That made MCS4+ TX work — but it parks the switch in a TX-favoring position that **disconnects RX path B's antenna on every 8812EU**: chain-B pwdb pins at the noise floor (~10 raw / −99 dBm) no matter which antenna is fitted. Nobody saw it because every RX validation to date — including #268's full-duplex proof and the 0x41e8 desense recheck — counts **total frames**, and chain A alone carries the stream. The loss is real, though: a dead chain B forfeits MRC diversity (3–5 dB, more in NLOS multipath). This PR replaces the static write with a port of the kernel's actual mechanism: `_efem_pinmux_config` (rtl8822e_halinit.c, rfe 21–24) → halmac `pinmux_set_func_8822e` for `RFE_CTRL_3/5/7/8/9/11`, transcribed entry-for-entry from `halmac_gpio_8822e.c`. GPIO13 becomes `WL_DPDT_SEL` driven by the RFE engine's RFE_CTRL_9, so the transfer switch follows TX/RX **in hardware**: PA on TX, both LNAs on RX — kernel parity. ## Evidence Two-adapter bench (BL-M8812EU2 ↔ 8812EU, ch149, 20 MHz, MCS5 at 6 Mbps offered unless noted, txagc 30 near-field, per-chain rssi/snr from the phystatus via a per-chain-reporting RX tool). Same card, back-to-back bring-ups, only the DPDT route changing: | DPDT route | RX chain A | RX chain B | TX @ MCS7 (10M offered) | |---|---|---|---| | static `0x4c[24]`+`[22]`-clear (current) | −68 dBm / 27 dB | **−99 dBm / dead** | 9.9M goodput, clean | | `0x4c[24]` only (halmac `WL_DPDT_SEL` bit alone) | −68 / 27 | −77 / 19 | **airs nothing** | | eFEM pinmux (this PR) | −70 / 26 | **−77 / 19** | **9.7M goodput, clean** | - The bit-level bisect that found it: with the full merge in place, skipping only the DPDT block brings chain B back; skipping only the 1SS TX mapping does not. Within the block, keeping `[22]` untouched revives chain B (and breaks TX — hence neither bit combination is sufficient; the pin needs its function claim). - Read-back before the write shows `0x4c[22]` SET from reset (`0x0062e282`), i.e. GPIO13 parked as BT_WAKE — which is also why `[24]`-only TX airs nothing: no one has claimed the pin for the RFE engine. The missing piece is the pin-function claim (`0x42[1]` on GPIO13 + `0x43[3]/[4]` fixups), which only the full pinmux walk performs. - Validated with both link ends running this port **simultaneously**: TX MCS7 9.7M / MCS5 6.0M at 0.0% loss / MCS0 clean, while RX reports both chains live (−70/−77 dBm, snr 26/19) — the combination that was previously impossible. - 0x41e8 history (quirks doc updated): the register is exonerated by direct A/B; the "RX desense" it was blamed for was this antenna disconnection all along, and both prior rechecks were blind to it because they counted total frames. ## Implementation notes - `RtlJaguar3Device::efem_pinmux_8822e()`: per-pin priority-list walk (halmac semantics — disable every claim above the target: `field := ~val & msk`; enable the target: `field := val & msk`; stop) + the WL/BT ownership fixups, as data tables. Kernel call order preserved (RFE_CTRL_3, 5, 7, 8, 9, 11). - Runs post-coex in `InitWrite` — coex `GPIO_MUXCFG` writes would mask it; same ordering lesson as the existing RFE pad block. The `PAD_CTRL1[29:28]` post-coex re-assert is kept. - The old routes remain selectable for board A/B: `DEVOURER_DPDT_MODE=legacy|bit24|skip` (default `efem`). - `docs/8822e-quirks.md` updated: the pin-mux section now describes the hardware-controlled route and its A/B matrix, and adds the honest moral — per-chain RSSI is the only trustworthy RX-health metric on a 2T2R part. ## Suggested follow-up (not in this PR) `GetActiveRxPaths`/per-chain health could gate CI-style validation so a single-chain regression can't hide behind total-frame counts again. 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com> Co-authored-by: Dmitry Ilyin <6576495+widgetii@users.noreply.github.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes the reproducible half of #238 on the reference hardware (LB-LINK BL-M8812EU2, rfe-21) and lands the issue's deliverable,
docs/8822e-quirks.md. Everything below is hardware-validated on the two-adapter bench (8812EU ↔ 8822CU, plus the vendor kernel and an in-tree rtw88 ground as controls); reproducers ship undertests/.Root causes & fixes
REG_LED_CFG 0x4c[24]=BIT_DPDT_WLBT_SELset,[22]clear, applied post-coex;txpath_1sssemantics):0x820=0x32(1ss-B) at 5 GHz /0x31at 2.4 GHz. The old 1ss-on-both mapping interacts with the DPDT fix (MCS0 wedges).Result: MCS7 goes 0 → ~5000 clean/12 s EVM −48; full ladder MCS0/4/7/54M clean; 40 MHz 5226 @ −53; 80 MHz TX verified against an independent kernel ground (5658 frames).
0x41e8path-B RX-desense) RESOLVED: it was a downstream artifact of the same front-end mis-config, not TSSI asymmetry (tests/eu_41e8_desense_recheck.sh: poking the ref with RX live = −2 %, noise). The structural skip is retired — path-B OFDM TX power is controllable in TX+RX mode again (full-duplex proof: 24k RX frames + 14.3k clean MCS7 simultaneously,tests/eu_fullduplex_pathb_check.sh).0x0writes silently no-op'd through the direct BB window — routed through the legacy FON port0x1808/0x4108per the kernel, in both halrf impls AND the radio-table loader (the vendor tables carry reg-0 entries). Verified via the TXGAPK gain-table readback (all-zero before, kernel-shaped ramps after).PAD_CTRL1[29:28]re-asserted there too.Kernel channel/runtime obligations ported (systematic source diff vs
reference/rtl88x2eu)PhydmRuntimeJaguar3): FA/CCA statistics, unlinked DIG, CCK-PD type4, EDCCA tracking — with one hardware-forced deviation: the DIG floor is0x1e, not phydm's generic0x1c(at0x1cthe 8822CU's dense-QAM RX decodes 0 of 65k MCS7 frames;0x1epasses 65.9k — same class as the kernel's per-IC "For HW setting" exceptions). RX-only sessions tick from a dedicated thread (register I/O on the bulk-IN event thread starves its own event loop).Verified non-gaps & closures
0x1e7c[30]=0under both drivers); thermal LUT already at parity; TX descriptor byte-identical (48 B).tests/eu_matched_power_evm.sh, refs bit-identical): MCS7 median −60 under both drivers.RF 0x1aTX_CCK_IND held post-IQK in full and fast paths).Out of scope / follow-ups
0bda:e822) and board variance: blocked on hardware (documented in the quirks doc).ctest 19/19 across the rebase onto current master; per-chip config subsets build (spot-checked no-Jaguar3).
🤖 Generated with Claude Code